/*
_________________            __________________________

Copyright (c) 2008 Arnaud Deredec & Quentin Dubois

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
* A simple AS3 library based on AlivePDF. 
* helps to create PDF out of a complex component, Parse HTML.... 
* 
* @author Arnaud Deredec & Quentin Dubois
* @version 0.1 Current Release
* @url http://code.google.com/p/pdfutil/
*/

package org.PDF.PDFUtil
{
        import flash.events.Event;
        import flash.events.TimerEvent;
        import flash.geom.ColorTransform;
        import flash.geom.Rectangle;
        import flash.utils.Timer;
        
        import mx.core.Container;
        import mx.events.FlexEvent;
        
        import org.PDF.Fonts.MyFontMetrics;
        import org.PDF.Html.HtmlEntities;
        import org.alivepdf.colors.RGBColor;
        import org.alivepdf.display.Display;
        import org.alivepdf.layout.Layout;
        import org.alivepdf.layout.Orientation;
        import org.alivepdf.layout.Size;
        import org.alivepdf.layout.Unit;
        import org.alivepdf.pages.Page;
        import org.alivepdf.pdf.PDF;
        import org.alivepdf.saving.Download;
        import org.alivepdf.saving.Method;
        
        [Event(name='complete', type='flash.events.Event.COMPLETE')]
		/**
		 * Main class to create PDF using Alive PDF library
		 * 
		 */
        public class PdfUtil
        {
               private var _WidthMM:Number ;
               private var _heightMM:Number ;
               private var _WidthPx:Number ;
               private var _heightPX:Number ;
               private var _WidthIn:Number ;
               private var _heightIn:Number ;
               
               private var _htmlTransform:HtmlEntities = new HtmlEntities();
               
               private var _fontMetrics:Object = new MyFontMetrics();

			   private var _thmManager:ThmManager = new ThmManager();

               private var _pdf:PDF;

               public function set pdf(p:PDF):void{
                 _pdf = p;
               }

               public function get pdf():PDF{
                 return _pdf;
               }
			
				public function setThmLib(lib:Object, thmId:String=null):void
				{
					_thmManager.setThmLib(lib, thmId);
				}
				
			   public function loadPageThm(thm:*, destContainer:*):void
			   {
			   		if (_thmManager.destContainer == null)
			   			_thmManager.destContainer = destContainer;		   			
			   		_thmManager.addThm(thm);
			   }
			   
			   public function pdfThmClean(e:Event=null):void
			   {
			   		_thmManager.clean();
			   }
			   
			   private var currentThmId:String;
			   private var myTimer:Timer = new Timer(500); 
			   private var currentThm:*;
			   
			   private var isPageFinished:Boolean = false;
			   
			   public function addPageUsingThm(thmId:String, data:Object):void
			   {
			   		currentThm = _thmManager.getThm(thmId);
			   		isPageFinished = false;
			   		currentThm.data = data;
			   		currentThmId = thmId;			   					   		
			   		(currentThm as Container).callLater(startTimer);			   				   		
			   }
			   
			   private function startTimer():void
			   {
			   		myTimer.start();
			   		myTimer.addEventListener(TimerEvent.TIMER, savePageThm);
			   }
			   
			   public function getPageFinished():Boolean
			   {
			   		return isPageFinished;
			   }
			   
			   public function savePageThm(e:Event=null):void
				{
					myTimer.stop();
					myTimer.removeEventListener(TimerEvent.TIMER, savePageThm);								
					this.addPagePDF(currentThm, currentThm.width, currentThm.height);															
					isPageFinished = true;
				}

				/**
				 * Create a new PDF object
				 * 
				 * @param pxw Pixel Width
				 * @param pxh Pixel Height
				 * @param f Function to use when the event COMPLETE is catch
				 * 
				 */
               public function PdfUtil(pxw:Number,pxh:Number,f:Function=null)
               {                                    
                   var s:Size = new Size([pxw, pxh], "", [0, 0],[0, 0] );
                                      
				   _pdf = new PDF( Orientation.PORTRAIT, Unit.POINT, s); 
                   _pdf.setDisplayMode( Display.FULL_PAGE, Layout.SINGLE_PAGE );
                   
                   if (f != null ){
                         _pdf.addEventListener(Event.COMPLETE,f);
                   }
               }

				/** 
				 * Save PDF to the Remote URL and Download it
				 * 
				 * @param url The remote url
				 * @param docName Doc name to use
				 */
               public function save(url:String,docName:String):void{ 
                  _pdf.save(Method.REMOTE,url,Download.INLINE,docName);
               }
			
				/**
				* Add a page to the current PDF
				*
				* @param pageW Page Width
				* @param pageH Page Height
				* 				
				*/				 
			   public function newPage(pageW:int=0,pageH:int=0):void
			   {
			   		var s:Size = new Size([pageW, pageH], "", [0, 0],[0, 0] );
					var newpage:Page = new Page ( Orientation.PORTRAIT, Unit.POINT,s );				
	               _pdf.addPage(newpage);
			   }

				/**
				 * Add a page to the current PDF based on a Flex Objet
				 * (ex : Canvas)
				 * 
				 * @param page Root flex object 
				 */
               public function addPagePDF(page:*,pageW:int=0,pageH:int=0):void{

	                if (pageW == 0){
	                   		pageW = page.width;
	                   		pageH = page.height;
	                }
	                var obArray:Array = page.getChildren();
					
					var s:Size = new Size([pageW, pageH], "", [0, 0],[0, 0] );
					var newpage:Page = new Page ( Orientation.PORTRAIT, Unit.POINT,s );				
	               _pdf.addPage(newpage);
	
	               for (var i:int=0;i<obArray.length;i++)  {
	               		if(obArray[i].visible) {
							addObjToPDF(obArray[i]);
	               		}
					}
               }
               
               /**
               * Add a page to the current PDF based on HTML
               * 
               * @param html HTML code
               * @param pageW Page width
               * @param pageH Page height 
               * @param x Starting x position
               * @param y Starting y position
               */
               public function addHTMLPagePDF(html:String,pageW:int=0,pageH:int=0, x:int=0, y:int=0):void{
	                				
					var s:Size = new Size([pageW, pageH], "", [0, 0],[0, 0] );
					var newpage:Page = new Page ( Orientation.PORTRAIT, Unit.POINT,s );				
	               _pdf.addPage(newpage);
	               	               
	               this.HtmlArray2Pdf(this.Html2Array(html), x, y);
               }
               
               private var _htmlBoxWidth:int;
               private var _tabSize:int = 10;
               private var _rgbC:RGBColor;     
               private var _margin:int = 10;
               /**
               * Add HTML content to the current PDF page
               * 
               * @param html HTML code
               * @param x Starting x position
               * @param y Starting y position
               * @param boxWidth Content size in pixel (default 0 = maximum size)
               * 
               */          
               public function addHTMLContent(html:String, x:int=0, y:int=0, boxWidth:int=0):void{
               	
               	   if (_pdf != null && _pdf.getCurrentPage() != null)
               	   {               	   	
               	   	if (boxWidth == 0)
               	   		_htmlBoxWidth = _pdf.getCurrentPage().width - _margin;
               	   	else
               	   		_htmlBoxWidth = boxWidth + x;
               	   		
               	   	this.HtmlArray2Pdf(this.Html2Array(html), x, y);	
               	   }  
               }                                            

				/**
				 * Add a Flex object content to the current PDF page
				 * 
				 * @param obj The Flex object
				 * @param addX Starting X position
				 * @param addY Starting Y position
				 * @param initPos True indicates that the first object is drawed at addX - addY
				 * 				 
				 */
               public function addObjToPDF(obj:*,addX:int=0,addY:int=0, initPos:Boolean=false):void
               {
					var fsize:int;
					var spaceh:Number;
					var align:String;
					var arrO:Array;
					
					var x:Number;
					var y:Number;
					var w:Number = obj.width;
					var h:Number = obj.height;
				
					if (initPos)
					{
						x = addX;
	               		y = addY;
					}
					else
					{
						x = (obj.x + addX);
	               		y = (obj.y + addY);
	    			}

					var finalClassName:String;
				   //For specific composents extending Flex object
				   switch(obj.className) {
					   case "obTitre":
					   	finalClassName = "Image";
					   	break;
					   case "obComp":
					   	finalClassName = "Image";
					   	break;			   		
					   case "obDesign":
					   	finalClassName = "Canvas";
					   	break;			   		
					   case "obIdent":
					   	finalClassName = "Canvas";
					   	break;
					   default:
					   	finalClassName = obj.className;
					   	break;
				   }


	               switch(finalClassName) {
	               	   //IMAGEs
		               case  "Image" :
		               	   _pdf.addImage( obj, x, y);	                   
		              		break;
		              		
		               case  "SmoothImage" :               
			               	/*
			               	var data:BitmapData = (obj.content as Bitmap).bitmapData;
			               	var encode:JPEGEncoder = new JPEGEncoder();
			               	var bArray:ByteArray = encode.encode(data);
			               	_pdf.addImageStream( obj.getBytes(), x, y, w, h);
			               	*/             		               
		                    _pdf.addImage( obj.bitmap, x, y, w , h);	            
		               		break;
		               		
		               case  "PieChart" :
		                   _pdf.addImage( obj, x, y);
		                   //_pdf.addImage( obj, 1.0, null, null, false, ImageFormat.JPG ,100,x,y);
		               		break;
		               		
		               case  "SmoothImageAsync" :               	               	            
			                _pdf.addImageStream( obj.getBytes(), x, y, w, h);                    
		               		break;
		               		
		               case  "PieChart" :
		                   _pdf.addImage( obj, x, y);	                   
		               		break; 
		               		              
		               case  "ColumnChart":
		                    _pdf.addImage( obj, x, y);
		               		break;
		               		
		               // BOX....
		               case  "HBox"  :
						  setHboxStyle(obj);
						
						  _pdf.drawComplexRoundRect( new Rectangle ( x, y, w, h ),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"));
						  _pdf.end();								
						
						  arrO =  obj.getChildren();
						  for (var i:int=0;i<arrO.length;i++)  {
						 	if (arrO[i].visible)
						   	 addObjToPDF(arrO[i], 1, y + 1);
						  }                   
						  break;
						  
		               case  "VBox" :
		                    setHboxStyle(obj);
		                    _pdf.drawRect ( new Rectangle ( x, y, w, h ) );
		                    _pdf.end();
		                    
			                 arrO =  obj.getChildren();
			                 for (var j:int=0;j<arrO.length;j++)  {
			                 	if (arrO[j].visible)
			                   	 addObjToPDF(arrO[j],x + 1,y + 1);
			                 }                    
		               		break;
		               		
		               case  "Canvas" :                 
		                  setHboxStyle(obj);
		                  if (obj.getStyle("borderStyle") != null && obj.getStyle("borderStyle") != "none")
		                  {
		                  	if (obj.getStyle("backgroundColor") != null && obj.getStyle("backgroundColor") != "")
		                  	{
		                  		var rgbC2:RGBColor = getColor(obj.getStyle("backgroundColor"));
		                  	    _pdf.beginFill ( rgbC2 );
		                  	}
		                 	_pdf.drawComplexRoundRect( new Rectangle ( x, y, w, h ),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"),
						  								obj.getStyle("cornerRadius"));
						    _pdf.endFill();
						  								
		                  }
		                 
		                 arrO =  obj.getChildren();
		                 for (var l:int=0;l<arrO.length;l++)  {
		                 	if (arrO[l].visible)
		                   	 addObjToPDF(arrO[l], x + 1,  y + 1);
		                 }
		               	 break;
		               		
		               case  "miniCV" :	                 
		                  setHboxStyle(obj);
		                 _pdf.drawRect ( new Rectangle ( x, y, w, h ) );
		                 _pdf.end();
		                 arrO =  obj.getChildren();
		                 for (var k:int=0;k<arrO.length;k++)  {
		                   addObjToPDF(arrO[k],x,y);
		                 }
		               	 break;
		               	 
		               // LIGNEs
		               case  "HRule":
		                   setLineStyle(obj);
		                  _pdf.moveTo ( x, y );
		                  _pdf.lineTo ( x+w, y);
		                  _pdf.end();
		               	  break;
		               	  
		               case  "VRule " :
		                  setLineStyle(obj);
		                  _pdf.moveTo ( x, y );
		                  _pdf.lineTo ( x+w, y);
		                  _pdf.end();
		                  break;
		
		               // TEXT
		               case  "Text":
		               	 addHTMLContent(obj.htmlText, x, y, w);
		                 /*
		                 align = "J";	                 
		                 setFont(obj);
		                 _pdf.setXY(x, y);
		                 fsize = obj.getStyle("fontSize");
		                 spaceh = fsize + 1;                 
						
		                 if (obj.getStyle("textAlign") == "center")
		                     align = "C" ;
		                 else if (obj.getStyle("textAlign") == "right")
		                   align = "R";
		
		                 _pdf.addMultiCell(w,spaceh,obj.text,0,align);
						*/
		               	 break;
		               
		               case  "UITextField":
						 addHTMLContent(obj.htmlText, x, y, w);
						 //_pdf.addImage(obj, x, y);
		               	 break;	               
		               
		               case  "Label" :  	               
	                   	  /*
	                   	  var fontStyle:String = "";
	                   	  if (obj.getStyle("fontWeight") == "bold")
	                   	  	fontStyle = "B";
						  var stringWidth:Number = getStringWidth(obj.getStyle("fontFamily"), fontStyle, Number(obj.getStyle("fontSize")), obj.text);
						  
						  if (obj.getStyle("textAlign") == "center")
		                    x += (Number(obj.width) - stringWidth) / 2;
		                 else if (obj.getStyle("textAlign") == "right")
		                 	x += (Number(obj.width) - stringWidth);
						                       
	                      setFont(obj);
	                      _pdf.addText(obj.text,x, (y + obj.getStyle("fontSize")));
						 */
						  addHTMLContent(obj.htmlText, x, y, w);
		               	  break;
		               	  
		               default :
		               		if (obj.hasOwnProperty("getChildren"))
		               		{
			              		arrO =  obj.getChildren();
			              		for (var m:int=0;m<arrO.length;m++)  
				                    addObjToPDF(arrO[m], 1 ,y);				             
		               		}
		               	 	break;
	               	   }
               }				

				/**
				 * Define line style
				 * 
				 * @param line Flex object
				 * 
				 */
               private  function setLineStyle(line:*):void{
                   var rgbC:RGBColor = getColor(line.getStyle("strokeColor"));
                   var lineSize:Number = line.height / 3 ;
                   _pdf.lineStyle ( rgbC,lineSize, 1, 1);
                   //_pdf.lineStyle ( rgbC,lineSize, 1, null);

               }

				/**
				 * Define Hbox Style
				 * 
				 * @param hbox Flex object
				 * 
				 */
               private  function setHboxStyle(hbox:*):void{
                   var lineSize:Number  = hbox.getStyle("borderThickness") ;
                   var rgbC:RGBColor = getColor(hbox.getStyle("borderColor"));
                   var color:String = hbox.getStyle("backgroundColor");
                   lineSize = lineSize / 3;
                   _pdf.lineStyle ( rgbC,lineSize, 1, 1);
                   //_pdf.lineStyle ( rgbC,lineSize, 1,null);

                   if (color != null) {
                       var rgbC2:RGBColor = getColor(hbox.getStyle("backgroundColor"));
                       _pdf.beginFill ( rgbC2 );
                   }  else {
                   	_pdf.endFill();
                   }


               }

				/**
				 * Define Font style
				 * 
				 * @param text Flex text object
				 * 
				 */
               private  function setFont(text:*):void{
                   var fontFamily:String = text.getStyle("fontFamily") ;
                   var fontSize:int  = text.getStyle("fontSize") ;
                   var fontWeight:String  = text.getStyle("fontWeight") ;

                   var rgbC:RGBColor = getColor(text.getStyle("color"));

                   var fontStyle:String = "";
                   if (fontWeight == "bold")
                                   fontStyle = "B";
                   _pdf.textStyle(rgbC) ;
                   _pdf.setFont( "Arial" , fontStyle, fontSize);

               }

				/**
				 * Create an RGBColor object from a color code
				 * 
				 * Handle HTML color notation
				 * 
				 * @param c Color code
				 * 
				 * @return Color Object
				 * 
				 * @example
				 * How to use it with HTML color :
				 * 
				 * <div class="listing">
				 * <pre>
				 * 	var myColor:RGBColor = this.getColor("#FFFFFF");
				 * </pre>
				 * </div>
				 * 
				 */
               private  function getColor(c:*):RGBColor{
                   var colorTrans:ColorTransform = new ColorTransform();
                   
                   if (c is String)
                   	colorTrans.color = new uint(Number((c as String).replace(/#/g, '0x')));
                   else
                   	colorTrans.color = c;
                   var rgbC:RGBColor = new RGBColor(Number(colorTrans.color));                   
                   return rgbC;
               }

				/**
				 * Split HTML code to Key Array
				 * 
				 * @param html HTML code
				 * 
				 * @return Array of HTML keys
				 * 
				 * @example
				 * <div class="listing">
				 * <pre>
				 * 	this.Html2Array("<p>Test</p>");
				 * </pre>
				 * will result in :
				 * <pre>
				 * 	["<p>", "Test", "</p>"]
				 * </div>
				 */					
				private function Html2Array(html:String):Array
				{		
					var htmlArray:Array = new Array();
					var lastIndex:Number = 0;							
					var first:Boolean = true;												
					var pattern:RegExp = /<(.*?)>/gi;
					var result:Array = pattern.exec(html);

					while (result != null) {				
						
						if (result.index != 0 && first)
							htmlArray.push(html.substr(0, result.index));
																																					
						first = false;
						
						htmlArray.push(result[0]);												
						
						lastIndex = pattern.lastIndex;												
						result = pattern.exec(html);
						
						/* si result different de null) */
						if (result != null && (result.index - lastIndex) != 0)
							htmlArray.push(html.substr(lastIndex, (result.index - lastIndex)));
					}
					
					if (lastIndex < html.length)
						htmlArray.push(html.substr(lastIndex, (html.length - 1)));
					
					//trace(htmlArray);
					return htmlArray;
				}		
				
				
				/**
				 * Copy an properties HTML object to a new One					
				 * 
				 * @param value Properties object
				 * 
				 * @return The new properties object
				 */ 
				private function copyProps(value:Object):Object
				{
					var copy:Object = new Object();
					copy["fontFamily"] = value["fontFamily"];
					copy["fontSize"] = value["fontSize"];						
					copy["fontStyle"] = value["fontStyle"];   
					copy["color"] = value["color"];
					copy["align"] = value["align"];
					copy["underlined"] = value["underlined"];
					copy["href"] = value["href"];			
					return copy;
				} 
				
				/**
				 * Add a new linebreak in PDF (only works with HTML content)
				 * 
				 * @param currentY The current Y position
				 * @param lineSize The line size to use
				 * 
				 * @return The new Y position
				 * 
				 */				
				private function newHTMLLine(currentY:int, lineSize:int):int
				{
					currentY += lineSize + 2;
					var maxHeight:int = _pdf.getCurrentPage().height - _margin;
					if (currentY > maxHeight)
					{
						currentY = lineSize + _margin;
						var newpage:Page = new Page ( _pdf.getCurrentPage().orientation, Unit.POINT, _pdf.getCurrentPage().size );				
	               		_pdf.addPage(newpage);
					}
					
					return currentY;
				}
				
				/**
				 * Transform an HTML Array into PDF content
				 * 
				 * @param html HTML Array
				 * @param x Starting X position
				 * @param y Starting Y position
				 * @param indexStart Current Index for reading html Array (for recursives calls)
				 * @param closeToken Close HTML token
				 * @param properties HTML properties of the previous node
				 * 
				 * @return Object containing { new index , new X position, new Y position }
				 * 
				 */
				private function HtmlArray2Pdf(html:Array, x:int=0, y:int=0, indexStart:int=0, closeToken:String="", properties:Object=null):Object
				{			
					
					var _saveX:Number = x;
					var _saveY:Number = y;	
					if (properties == null)
					{
						properties = new Object();
						properties["fontFamily"] = "Arial";
						properties["fontSize"] = "10";
						properties["underlined"] = "N";												
						properties["fontStyle"] = " ";
						properties["href"] = " ";
						properties["align"] = "LEFT";   
						properties["color"] = "000";	                    
					}
															
					var _currentProperties:Object = copyProps(properties);								
					var _return:Object;
					
					var text:String;
					for(var i:int = indexStart; i < html.length; i++)
					{
						text = html[i] as String;
						/* si c'est la balise fermante on remonte a la fonctio d'appel */
						if (text.toUpperCase() == closeToken)
							return { i:i, newx:x, newy:y };
						
						/* c'est un texte */	
						if (text.charAt(0) != "<")
						{		                   			                   		                   			                   			                   	
		                   	_pdf.setFont( _currentProperties["fontFamily"] as String , 
                   		   				((_currentProperties["fontStyle"] as String) == " ") ? "":(_currentProperties["fontStyle"] as String) , 
                   		   				Number(_currentProperties["fontSize"]));														

							_rgbC = getColor(_currentProperties["color"]);
							_pdf.textStyle(_rgbC) ;
							
							text = _htmlTransform.Html2Chars(text);
							
		                   	var _length:Number = getStringWidth(_currentProperties["fontFamily"],
		                   										(_currentProperties["fontStyle"] == " ") ? "":_currentProperties["fontStyle"],
		                   										Number(_currentProperties["fontSize"]),
		                   										text);
		                   	
		                   	var w:int = _htmlBoxWidth;
		                   	var possiblechar:Number;
		                   	var limit:int = 0;
		                   	var textLength:int = text.length;
		                   	var pxSubLength:int;
		                   	var _moyPxChar:Number = _length / textLength;

		                   	for (var start:int=0; start < textLength; )
		                   	{
		                   		/* How namy characters can fit available space */
		                   		possiblechar = (w - x) / _moyPxChar;
		                   		limit = Math.round(possiblechar);
		                   		if (limit > text.substr(start).length)
		                   			limit = text.substr(start).length;		                   			

								var tmpLimit:Number = limit;
								
								while(text.charAt(start + tmpLimit) != ' ' && tmpLimit > start && tmpLimit != text.substr(start).length)
								{
									tmpLimit--;
								} 
								if (tmpLimit != start)
								{
									limit = tmpLimit;								
								}

		                   		/* Managing alignment */	
		                   		if (_currentProperties["align"] == "CENTER")
		                   			x += Math.round( ((w - x) / 2) - (int((limit * _moyPxChar) / 2)));
								else if	(_currentProperties["align"] == "RIGHT")
									x = Math.round(w - (limit * _moyPxChar));



		                   			
		                   		_pdf.addText(text.substr(start, limit), x, y);

		                   		pxSubLength = getStringWidth(_currentProperties["fontFamily"],
               										(_currentProperties["fontStyle"] == " ") ? "":_currentProperties["fontStyle"],
               										Number(_currentProperties["fontSize"]),
               										text.substr(start, limit));
								
								if (_currentProperties["href"] != " ")
									_pdf.addLink(x, y - Number(_currentProperties["fontSize"]), pxSubLength, Number(_currentProperties["fontSize"]), _currentProperties["href"]);	                   		
		                   		if (_currentProperties["underlined"] == "Y")
		                   		{
		                   			_pdf.moveTo ( x , y + 1 );
                  					_pdf.lineTo ( x + pxSubLength, y + 1);
                  					_pdf.end();
		                   		}
		                   		
		                   		x += pxSubLength;
		                   		start += limit;
		                   		
		                   		if (start < textLength)
	                   			{
	                   				x = _saveX;
	                   				y = newHTMLLine(y, Number(_currentProperties["fontSize"]));	                   				
	                   			}		
		                   	}		                  		                   			                   
						}
						else if (text.toUpperCase().indexOf('<BR') != -1)
						{
							y = newHTMLLine(y, Number(_currentProperties["fontSize"]));							
							x = _saveX;							
						}
						else if (text.toUpperCase().indexOf('<HR') != -1)
						{		                   								
							y = newHTMLLine(y, Number(_currentProperties["fontSize"]));
							x = _saveX;														
							
							_pdf.moveTo ( x, y );
                  			_pdf.lineTo ( _pdf.getCurrentPage().width - 1, y);
                  			_pdf.end();
						}					
						else if (text.toUpperCase().indexOf('<A') != -1)
						{
							_return = HtmlArray2Pdf(html, x, y, i+1 , "</A>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));
							i = _return["i"] as int;
							x = _return["newx"] as int;							
							y = _return["newy"] as int;
						}
						else if (text.toUpperCase().indexOf('<U') != -1)
						{
							_return = HtmlArray2Pdf(html, x, y, i+1 , "</U>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));							
							i = _return["i"] as int;
							x = _return["newx"] as int;							
							y = _return["newy"] as int;
						}						
						else if (text.toUpperCase().indexOf('<B') != -1)
						{
							_return = HtmlArray2Pdf(html, x, y, i+1 , "</B>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));							
							i = _return["i"] as int;
							x = _return["newx"] as int;							
							y = _return["newy"] as int;
						}
						else if (text.toUpperCase().indexOf('<FONT') != -1)
						{
							_return = HtmlArray2Pdf(html, x, y, i+1 , "</FONT>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));							
							i = _return["i"] as int;	
							x = _return["newx"] as int;							
							y = _return["newy"] as int;						
						}
						else if (text.toUpperCase().indexOf('<I') != -1)
						{
							_return = HtmlArray2Pdf(html, x, y, i+1 , "</I>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));							
							i = _return["i"] as int;
							x = _return["newx"] as int;							
							y = _return["newy"] as int;
						}
						else if (text.toUpperCase().indexOf('<LI') != -1)
						{	
							x += _tabSize;					
							y = newHTMLLine(y, Number(_currentProperties["fontSize"]));
							
							_pdf.drawCircle(x, y - (Number(_currentProperties["fontSize"]) / 2) + 2  , 1);
							x += _tabSize;

							_return = HtmlArray2Pdf(html, x, y, i+1 , "</LI>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));
							i = _return["i"] as int;							
							y = _return["newy"] as int;
							x = _saveX;							
						}						
						else if (text.toUpperCase().indexOf('<P') != -1)
						{
							x = _saveX;							
							y = newHTMLLine(y, Number(_currentProperties["fontSize"]));

							_return = HtmlArray2Pdf(html, x, y, i+1 , "</P>", mixProperties(getHtmlProperties(text.toUpperCase()), _currentProperties));
							i = _return["i"] as int;
							y = _return["newy"] as int;							
							x = _saveX;						
						}																		
					}
					return { i:html.length, newx:x, newy:y };				
				}
				
				/**
				 * Mix previous HTML properties with new properties
				 * 
				 * nb : Specific case for fontStyle
				 * 
				 * @param newProperties New properties
				 * @param mainProperties Current properties
				 * 
				 * @return The mixed properties object
				 * 
				 **/
				private function mixProperties(newProperties:Object, mainProperties:Object):Object
				{
					var mixed:Object = new Object();
					if (mainProperties == null)
						if (newProperties == null)
							return mixed;
						else
							return newProperties;
									
						if (newProperties["fontFamily"] != null)
							mixed["fontFamily"] = newProperties["fontFamily"];
						else
							mixed["fontFamily"] = mainProperties["fontFamily"];
							
						if (newProperties["href"] != null)
							mixed["href"] = newProperties["href"];
						else
							mixed["href"] = mainProperties["href"];							
							
						if (newProperties["fontSize"] != null)
							mixed["fontSize"] = newProperties["fontSize"];
						else						   
							mixed["fontSize"] = mainProperties["fontSize"];
							
						if (newProperties["align"] != null)
							mixed["align"] = newProperties["align"];
						else						   
							mixed["align"] = mainProperties["align"];							
							
						if (newProperties["color"] != null)
							mixed["color"] = newProperties["color"];
						else	
							mixed["color"] = mainProperties["color"];
							
						if (newProperties["underlined"] != null)
							mixed["underlined"] = newProperties["underlined"];
						else	
							mixed["underlined"] = mainProperties["underlined"];							
					
					
						var prop1:String = (mainProperties["fontStyle"] != null) ? mainProperties["fontStyle"]:" ";
						var prop2:String = (newProperties["fontStyle"] != null) ? newProperties["fontStyle"]:" ";
					
						if (prop1 != prop2)
						{
							mixed["fontStyle"] = prop1 + prop2;
							if ((mixed["fontStyle"] as String).length > 2)
								mixed["fontStyle"] = (mixed["fontStyle"] as String).substr(0, 2);
							if ((mixed["fontStyle"] as String).length == 2)
							{
								if ((mixed["fontStyle"] as String) == "IB")
									mixed["fontStyle"] = "BI";
								else if ((mixed["fontStyle"] as String) != "BI")
								{
									if ((mixed["fontStyle"] as String).charAt(0) != " ")
										mixed["fontStyle"] = (mixed["fontStyle"] as String).charAt(0);
									else
										mixed["fontStyle"] = (mixed["fontStyle"] as String).charAt(1);
								}		
							}
							
						}
						else
							mixed["fontStyle"] = prop1;	
					
					return mixed;
				}	
				
				/**
				 * Parse a HTML token to read the attributes
				 * 
				 * nb : Only simples attributes are read
				 * 
				 * @param htmlString The html token
				 * 
				 * @return The properties object
				 * 
				 **/
				private function getHtmlProperties(htmlString:String):Object
				{
					var props:Object = new Object();
					
					if (htmlString.toUpperCase().indexOf('<B') != -1)
						props["fontStyle"] = "B";
					else if (htmlString.toUpperCase().indexOf('<I') != -1)	
						props["fontStyle"] = "I";
					else if (htmlString.toUpperCase().indexOf('<A') != -1)
					{	
						props["fontStyle"] = "B";
						props["color"] = "00255";
					}
					else if (htmlString.toUpperCase().indexOf('<U') != -1)
					{
						props["underlined"] = "Y";
						props["fontStyle"] = " "
					}		
					else
						props["fontStyle"] = " ";
				
					var attri:Array = htmlString.split(' ');
					for (var i:int = 0; i < attri.length; i++)
					{
						var toTest:String = (attri[i] as String);
						if (toTest.charAt(0) == "<")
							continue;
						else if (toTest.charAt(toTest.length - 1) == ">")
							toTest = toTest.substr(0, toTest.length - 1);
							
						var args:Array = toTest.match('^([^=]*)=["\']?([^"\']*)["\']?$');
						if (args == null)
							continue;	
						
						//SIZE="3" ==> [0]= SIZE="3" / [1]= SIZE / [2]= 3	
						//TODO : Parser le style ?
						for (var j:int = 0; j < args.length; j+=3)
						{
							if (args[j+1] == "HREF")
								props["href"] = args[j+2];
							else if (args[j+1] == "ALIGN")
							{
								props["align"] = args[j+2];
								if (props["align"] != "CENTER" && props["align"] != "LEFT" && props["align"] != "RIGHT")
									props["align"] = "LEFT";
							}
							else if (args[j+1] == "SIZE")
								props["fontSize"] = args[j+2];	
							else if (args[j+1] == "COLOR")
								props["color"] = args[j+2];
							else if (args[j+1] == "FACE")
							{
								
								if ((args[j+2] as String).indexOf("ARIAL") != -1)
									props["fontFamily"] = "Arial";
								else if ((args[j+2] as String).indexOf("COURIER") != -1)
									props["fontFamily"] = "Courier";
								else if ((args[j+2] as String).indexOf("HELVETICA") != -1)
									props["fontFamily"] = "Helvetica";
								/*
								else if ((args[j+2] as String).indexOf("SYMBOL") != -1)
									props["fontFamily"] = "Symbol";
								*/
								else if ((args[j+2] as String).indexOf("TIMES") != -1)
									props["fontFamily"] = "Times";
								/*
								else if ((args[j+2] as String).indexOf("ZAPFDINGBATS") != -1)
									props["fontFamily"] = "Zapfdingbats";
								*/
								else								
									props["fontFamily"] = "Arial";																					
							}	
						} 
					}				
					return props;
				}
				
				/**
				 * Return a pixel size for a String
				 * 
				 * nb : From AlivePDF
				 * 
				 * @param fontName Font name in use
				 * @param fontStyle Font style in use (B / I / BI)
				 * @param fontSize Font size in use
				 * @param content The text to measure
				 * 
				 * @return Line size in pixel
				 * 
				 **/
				private function getStringWidth( fontName:String, fontStyle:String, fontSize:Number, content:String ):Number
				{		
					if( fontName.toLowerCase() == 'arial' ) fontName = 'helvetica';						
	
					var cw:Object = MyFontMetrics[fontName.toLowerCase() + fontStyle];
					var w:Number = 0;
					var l:int = content.length;
					var cwAux:Number = 0;
					
					while (l--) 
					
					{						
						cwAux = cw[content.charAt(l)] as Number;						
						if ( isNaN ( cwAux ) || cwAux == 0 ) cwAux = 580;					
						w += cwAux;					
					}					
					return w*fontSize/1000;
		
				}				
				
				/**
				 * Returns the PDF content as byte with Method.LOCAL from AlivePDF
				 * 
				 * @return Bytes
				 * 
				 */
				public function getBytes():*
				{
					return _pdf.save(Method.LOCAL);
				}
				
				/**
				 * 
				 * Interface for the addImage functio from AlivePDF to handle complex designs
				 * 
				 * @param obj Flex display Object / Image
				 * @param x Starting X position
				 * @param y Starting Y position
				 * 
				 */
				public function addImage(obj:*, x:int, y:int):void
				{
					_pdf.addImage(obj, x, y);
				}				
					
        }
}
